#include "znx_http_request.h"


znx_result_t
znx_http_seal_request_header(znx_http_request_t *request,
    znx_buf_t **header_buf_p)
{
    znx_str_t           *http_version_str;
    znx_http_header_t   *header, *tmp;
    size_t              n, offset;
    znx_queue_t         *q;
    znx_buf_t           *header_buf;

    if (proc_exit) {
        request->want_close = 1;
    }

    if (request->http_version == 0) {
        request->http_version = ZNX_HTTP_VERSION_11;
    }

    if (request->method.len == 0) {
        request->method.data = (u_char *)ZNX_HTTP_METHOD_GET;
        request->method.len = ZNX_HTTP_METHOD_GET_LEN;
    }

    if (request->http_version == ZNX_HTTP_VERSION_20) {
        ZNX_HTTP_LOG_FATAL(request->hc->hlog, "no implements");
    } else if (request->http_version == ZNX_HTTP_VERSION_QUIC) {
        ZNX_HTTP_LOG_FATAL(request->hc->hlog, "no implements");
    } else if (request->http_version == ZNX_HTTP_VERSION_9) {
        ZNX_HTTP_LOG_FATAL(request->hc->hlog, "no implements");
    } else if (request->http_version == ZNX_HTTP_VERSION_10) {
        http_version_str = &znx_http_version_10;
    } else {
        http_version_str = &znx_http_version_11;
    }

    if (request->request_uri.len == 0 || request->request_uri.data[0] != '/') {
        ZNX_HTTP_LOG_ERROR(request->hc->hlog, "invalid request_uri");
        return ZNX_HTTP_INVALID_REQUEST_URI;
    }

    int64_t content_length = -1;
    header = znx_http_headers_get(&request->headers, &znx_http_content_length_hdr_name, 0);
    if (header != NULL) {
        if (!znx_str_to_int64(&header->val, &content_length) || content_length < 0) {
            ZNX_HTTP_LOG_ERROR(request->hc->hlog, "invalid content length header");
            return ZNX_HTTP_INVALID_CONTENT_LENGTH_HEADER;
        }

        request->content_length = content_length;
        request->content_remain = content_length;
    }

    header = znx_http_headers_get(&request->headers, &znx_http_transfer_encoding_hdr_name, 0);
    if (header != NULL) {
        if (header->val.len == ZNX_HTTP_CHUNKED_LEN &&
            znx_strncasecmp(header->val.data, ZNX_HTTP_CHUNKED, ZNX_HTTP_CHUNKED_LEN) == 0)
        {
            request->chunked = 1;
        } else if (header->val.len != ZNX_HTTP_IDENTITY_LEN ||
            znx_strncasecmp(header->val.data, ZNX_HTTP_IDENTITY, ZNX_HTTP_IDENTITY_LEN) != 0)
        {
            ZNX_HTTP_LOG_ERROR(request->hc->hlog, "transfer-encoding: %v not implemented", &header->val);
            return ZNX_HTTP_ERR_NOT_IMPLEMENTED;
        }

        if (request->http_version == ZNX_HTTP_VERSION_10) {
            request->chunked = 0;
            znx_http_headers_del(&request->headers, &znx_http_transfer_encoding_hdr_name);
        }
    }

    if (request->content_length < 0 && !request->chunked) {
        request->nobody = 1;
    }

    header = znx_http_headers_get(&request->headers, &znx_http_connection_hdr_name, 0);
    if (header != NULL) {
        if (header->val.len == ZNX_HTTP_CLOSE_LEN ||
            znx_strncmp(header->val.data, ZNX_HTTP_CLOSE, ZNX_HTTP_CLOSE_LEN) == 0)
        {
            request->want_close = 1;
        }

        if (request->want_close) {
            header->val.len = ZNX_HTTP_CLOSE_LEN;
            header->val.data = (u_char *)ZNX_HTTP_CLOSE;
        }
    } else {
        if (request->want_close && request->http_version == ZNX_HTTP_VERSION_11) {
            header = znx_pool_calloc(request->pool, sizeof(znx_http_request_t));
            header->key.data = (u_char *)ZNX_HTTP_CONNECTION_HDR;
            header->key.len = ZNX_HTTP_CONNECTION_HDR_LEN;
            header->val.data = (u_char *)ZNX_HTTP_CLOSE;
            header->val.len = ZNX_HTTP_CLOSE_LEN;
            znx_http_headers_add(&request->headers, header);
        }
    }

    znx_http_headers_del(&request->headers, &znx_http_trailer_hdr_name);
    if (request->chunked && request->trailers) {
        n = 0;
        offset = 0;
        q = znx_queue_head(&request->trailers->headers.head);
        while (q != &request->trailers->headers.head) {
            header = znx_queue_data(q, znx_http_header_t, queue);
            n += header->key.len + 1;
            request->trailers->total_trailers++;
            q = q->next;
        }

        if (n > 0) {
            tmp = znx_pool_calloc(request->pool, sizeof(znx_http_header_t));
            tmp->key.data = (u_char *)ZNX_HTTP_TRAILER_HDR;
            tmp->key.len = ZNX_HTTP_TRAILER_HDR_LEN;
            tmp->val.data = znx_pool_malloc(request->pool, n);

            q = znx_queue_head(&request->trailers->headers.head);
            while (q != &request->trailers->headers.head) {
                header = znx_queue_data(q, znx_http_header_t, queue);
                znx_strncpy(tmp->val.data + offset, header->key.data, header->key.len);
                offset += header->key.len;
                *(tmp->val.data + offset) = ',';
                offset += 1;
                q = q->next;
            }

            if (offset > 0) {
                offset--;
                tmp->val.len = offset;
            }

            if (tmp->val.len > 0) {
                znx_http_headers_add(&request->headers, tmp);
            }

        }
    }

    n = 0;

    // METHOD + ' ' + request_uri + ' ' + HTTP/1.x + CRLF
    // eg: GET /1.txt HTTP/1.1\r\n
    n += request->method.len + 1 +
        request->request_uri.len + 1 +
        http_version_str->len + CRLF_LEN;

    // HEADER_NAME_STR + ':' + ' ' + HEADER_VAL_STR + CRLF
    // eg: Host: www.aaa.com\r\n
    q = znx_queue_head(&request->headers.head);
    while (q != &request->headers.head) {
        header = znx_queue_data(q, znx_http_header_t, queue);
        n += header->key.len + 1 + 1 + header->val.len + CRLF_LEN;
        q = q->next;
    }

    // CRLF
    n += CRLF_LEN;
    n += 32;

    header_buf = znx_buf_alloc_ext(n);
    *header_buf_p = header_buf;

    header_buf->end = znx_snprintf(header_buf->end,
        (size_t)(header_buf->data_end - header_buf->end),
        "%v %v %v\r\n",
        &request->method, &request->request_uri, http_version_str);

    q = znx_queue_head(&request->headers.head);
    while (q != &request->headers.head) {
        header = znx_queue_data(q, znx_http_header_t, queue);
        header_buf->end = znx_snprintf(header_buf->end,
            (size_t)(header_buf->data_end - header_buf->end),
            "%v: %v\r\n", &header->key, &header->val);
        q = q->next;
    }

    znx_strncpy(header_buf->end, CRLF, CRLF_LEN);
    header_buf->end += CRLF_LEN;
    return ZNX_SUCCESS;
}


znx_result_t
znx_http_send_request_header(znx_http_request_t *request)
{
    znx_http_conn_t             *hc;
    znx_result_t                res;

    hc = request->hc;

    if (!request->client_request) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "expect client request");
    }

    if (request->write_error != ZNX_SUCCESS) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "an error has occurred in http request");
    }

    if (request->header_sent) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "the request header has been sent");
    }

    request->header_sent = 1;

    znx_buf_t   *header_buf = NULL;
    res = znx_http_seal_request_header(request, &header_buf);
    if (res != ZNX_SUCCESS) {
        request->write_error = res;
        return res;
    }

    znx_http_conn_start_io(hc);

    size_t n = (size_t)(header_buf->end - header_buf->pos);
    res = znx_http_conn_write(hc, header_buf->pos, n);
    if (res > 0 && (size_t)(res) == n) {
        header_buf->pos += res;
        znx_buf_free(header_buf);
        return ZNX_SUCCESS;
    }

    znx_buf_chain_enqueue(request->output_chain, header_buf, ZNX_FALSE);

    if (res == 0) {
        request->write_error = ZNX_HTTP_SERVER_CLOSE_IN_SEND_REQUEST;
        return ZNX_HTTP_SERVER_CLOSE_IN_SEND_REQUEST;
    }

    if (res < 0) {
        request->write_error = ZNX_HTTP_SEND_REQUEST_FAILED;
        return ZNX_HTTP_SEND_REQUEST_FAILED;
    }

    return ZNX_SUCCESS;
}


static void
znx_http_request_body_move_in_chunked_transfer(znx_http_request_t *request)
{
    size_t                  todo, consume, n;
    znx_buf_t               *tmp_buf, *body_buf;

    // last chunk buf already write output_chain.
    if (request->write_body_done) {
        return;
    }

    todo = znx_buf_chain_total_bytes(request->body_chain);
    if (todo == 0) {
        return;
    }

    // add chunked size buf before moveing request body.
    tmp_buf = znx_buf_alloc_ext(128);
    tmp_buf->end = znx_snprintf(tmp_buf->end, 128, "%xd\r\n", todo);

    znx_buf_chain_enqueue(request->output_chain, tmp_buf, ZNX_FALSE);

    consume = 0;
    // move request body from request->body_chain to output_chain.
    body_buf = znx_buf_chain_first(request->body_chain);
    while (body_buf != NULL) {
        n = (size_t)(body_buf->end - body_buf->pos);
        if (n > 0) {
            tmp_buf = znx_buf_alloc(body_buf->data, body_buf->pos, body_buf->end);
            znx_buf_chain_enqueue(request->output_chain, tmp_buf, ZNX_FALSE);
            consume += n;
        }

        body_buf = znx_buf_chain_next(request->body_chain, body_buf);
    }

    znx_buf_chain_consume(request->body_chain, consume);

    // add chunk data crlf.
    tmp_buf = znx_buf_alloc_ext(128);
    znx_strncpy(tmp_buf->end, CRLF, CRLF_LEN);
    tmp_buf->end += CRLF_LEN;
    znx_buf_chain_enqueue(request->output_chain, tmp_buf, ZNX_FALSE);
}


static znx_result_t
znx_http_send_request_body_in_chunked_transfer(znx_http_request_t *request)
{
    znx_buf_t               *buf;
    size_t                  n, todo, niov;
    znx_http_conn_t         *hc;
    znx_iovec_t             tiovec[ZNX_HTTP_MAX_WRITE_IOV];
    znx_result_t            res;

    hc = request->hc;

    znx_http_request_body_move_in_chunked_transfer(request);

    if (znx_buf_chain_total_bytes(request->output_chain) == 0) {
        if (request->last_body_flag) {
            request->send_body_done = 1;
            return ZNX_SUCCESS;
        }
        return ZNX_AGAIN;
    }

    for ( ;; ) {
        todo = 0;
        niov = 0;

        buf = znx_buf_chain_first(request->output_chain);
        while (niov < ZNX_HTTP_MAX_WRITE_IOV && buf) {
            n = (size_t)(buf->end - buf->pos);
            if (n == 0) {
                buf = znx_buf_chain_next(request->output_chain, buf);
                continue;
            }

            tiovec[niov].iov_base = buf->pos;
            tiovec[niov].iov_len = n;
            niov++;
            todo += n;

            buf = znx_buf_chain_next(request->output_chain, buf);
        }

        if (todo == 0) {
            if (request->last_body_flag) {
                request->send_body_done = 1;
                return ZNX_SUCCESS;
            }

            return ZNX_AGAIN;
        }

        if (niov == 1) {
            res = znx_http_conn_write(hc, tiovec[0].iov_base, tiovec[0].iov_len);
        } else {
            res = znx_http_conn_writev(hc, tiovec, (int)niov);
        }

        if (res == ZNX_AGAIN) {
            ZNX_HTTP_LOG_DEBUG(hc->hlog, "send request body eagain");
            return ZNX_AGAIN;
        }

        if (res == 0) {
            ZNX_HTTP_LOG_DEBUG(hc->hlog, "server close read in send request body");
            return ZNX_HTTP_SERVER_CLOSE_IN_SEND_REQUEST;
        }

        if (res < 0) {
            ZNX_HTTP_LOG_DEBUG(hc->hlog, "send request body failed, err: %d", res);
            return res;
        }

        znx_buf_chain_consume(request->output_chain, (size_t)res);
    }
}


static void
znx_http_request_body_move_in_cl_transfer_internal(znx_http_request_t *request)
{
    znx_buf_t               *body_buf;
    size_t                  consume, n;
    znx_buf_t               *tmp_buf;

    // last CRLF is written.
    // use write_body_done mark whether last CRLF is written.
    if (request->write_body_done) {
        return;
    }

    // move request body from request->body_chain to request->output_chain.

    consume = 0;
    body_buf = znx_buf_chain_first(request->body_chain);
    while (body_buf != NULL && request->content_remain > 0) {
        n = (size_t)(body_buf->end - body_buf->pos);
        if (n > (size_t)request->content_remain) {
            n = (size_t)request->content_remain;
        }

        if (n > 0) {
            tmp_buf = znx_buf_alloc(body_buf->data, body_buf->pos, body_buf->pos + n);
            znx_buf_chain_enqueue(request->output_chain, tmp_buf, ZNX_FALSE);
            consume += n;
            request->content_remain -= n;

        }

        body_buf = znx_buf_chain_next(request->body_chain, body_buf);
    }
    znx_buf_chain_consume(request->body_chain, consume);
}


static void
znx_http_request_body_move_in_cl_transfer(znx_http_request_t *request)
{
    znx_buf_t                   *last_buf;

    znx_http_request_body_move_in_cl_transfer_internal(request);

    // body is not finished.
    if (request->content_remain > 0) {
        return;
    }

    // use write_body_done mark whether last CRLF is written.
    if (request->write_body_done) {
        return;
    }

    request->write_body_done = 1;

    last_buf = znx_buf_alloc_ext(128);
    znx_strncpy(last_buf->end, CRLF, CRLF_LEN);
    last_buf->end += CRLF_LEN;
    znx_buf_chain_enqueue(request->output_chain, last_buf, ZNX_FALSE);
}


static znx_result_t
znx_http_send_request_body_in_cl_transfer(znx_http_request_t *request)
{
    znx_http_conn_t             *hc;
    znx_buf_t                   *buf;
    size_t                      n, todo, niov;
    znx_iovec_t                 tiovec[ZNX_HTTP_MAX_WRITE_IOV];
    znx_result_t                res;

    hc = request->hc;

    znx_http_request_body_move_in_cl_transfer(request);

    if (znx_buf_chain_total_bytes(request->output_chain)) {
        if (request->content_remain == 0) {
            request->send_body_done = 1;
            return ZNX_SUCCESS;
        }
        return ZNX_AGAIN;
    }

    for ( ;; ) {
        todo = 0;
        niov = 0;

        buf = znx_buf_chain_first(request->output_chain);
        while (niov < ZNX_HTTP_MAX_WRITE_IOV && buf) {
            n = (size_t)(buf->end - buf->pos);
            if (n == 0) {
                buf = znx_buf_chain_next(request->output_chain, buf);
                continue;
            }

            tiovec[niov].iov_base = buf->pos;
            tiovec[niov].iov_len = n;
            niov++;
            todo += n;

            buf = znx_buf_chain_next(request->output_chain, buf);
        }

        if (todo == 0) {
            if (request->content_remain == 0) {
                request->send_body_done = 1;
                return ZNX_SUCCESS;
            }

            return ZNX_AGAIN;
        }

        if (niov == 1) {
            res = znx_http_conn_write(hc, tiovec[0].iov_base, tiovec[0].iov_len);
        } else {
            res = znx_http_conn_writev(hc, tiovec, (int)niov);
        }

        if (res == ZNX_AGAIN) {
            ZNX_HTTP_LOG_DEBUG(hc->hlog, "send request body eagain");
            return ZNX_AGAIN;
        }

        if (res == 0) {
            ZNX_HTTP_LOG_DEBUG(hc->hlog, "server close read in send request body");
            return ZNX_HTTP_CLIENT_CLOSE_IN_SEND_RESPONSE;
        }

        if (res < 0) {
            ZNX_HTTP_LOG_DEBUG(hc->hlog, "send request body failed, err: %d", res);
            return res;
        }

        znx_buf_chain_consume(request->output_chain, (size_t)res);
    }
}


static znx_result_t
znx_http_do_send_request_body(znx_http_request_t *request)
{
    znx_http_conn_t             *hc;

    hc = request->hc;

    if (hc->c->write.timedout) {
        ZNX_HTTP_LOG_DEBUG(hc->hlog, "send request body timedout");
        hc->c->write.timedout = 0;
        return ZNX_HTTP_WRITE_BODY_TIMEDOUT;
    }

    if (request->http_version == ZNX_HTTP_VERSION_20) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "no implements");
        return ZNX_HTTP_ERR_NOT_IMPLEMENTED;
    } else if (request->http_version == ZNX_HTTP_VERSION_QUIC) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "no implements");
        return ZNX_HTTP_ERR_NOT_IMPLEMENTED;
    } else if (request->http_version == ZNX_HTTP_VERSION_9) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "no implements");
        return ZNX_HTTP_ERR_NOT_IMPLEMENTED;
    }

    if (request->chunked) {
        return znx_http_send_request_body_in_chunked_transfer(request);
    }

    if (request->content_length >= 0) {
        return znx_http_send_request_body_in_cl_transfer(request);
    }

    // assert(request->nobody)
    request->send_body_done = 1;
    return ZNX_SUCCESS;
}


static void
znx_http_send_request_body_handler(znx_http_conn_t *hc)
{
    znx_result_t            res;
    znx_http_request_t      *request;
    znx_http_core_conf_t    *cf;

    request = hc->data;
    cf = request->cf;

    res = znx_http_do_send_request_body(request);
    if (res == ZNX_AGAIN &&
        znx_buf_chain_total_bytes(request->output_chain) != 0)
    {
        znx_event_loop_add_timer(&hc->c->write, cf->write_request_body_timeout_ms);
        znx_http_enable_send_request_body(request);
        return;
    }

    if (res != ZNX_AGAIN && res != ZNX_SUCCESS) {
        request->write_error = res;
    }

    znx_http_disable_send_request_body(request);
    request->handler(request, res);
}


void
znx_http_start_send_request_body(znx_http_request_t *request,
    znx_http_request_handler_t notify_handler, void *data)
{
    znx_http_conn_t             *hc;

    hc = request->hc;

    if (!request->client_request) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "expect server request");
    }

    if (!request->header_sent) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "request header no sent");
    }

    if (request->start_send_body) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "repeat call start_send_body");
    }

    request->start_send_body = 1;

    if (request->write_error) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "an error has occurred in http request");
    }

    request->handler = notify_handler;
    request->data = data;

    if (request->nobody) {
        request->send_body_done = 1;
        notify_handler(request, ZNX_SUCCESS);
        return;
    }

    hc->write_handler = znx_http_send_request_body_handler;
    znx_http_send_request_body_handler(hc);
}


void
znx_http_enable_send_request_body(znx_http_request_t *request)
{
    znx_http_conn_t             *hc;

    hc = request->hc;

    if (!request->client_request) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "expect http client request");
    }

    if (!request->start_send_body) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "must exec start_send_request_body first");
    }

    if (!hc->write_active) {
        znx_http_conn_enable_write(hc);
    }

    if (!znx_http_conn_write_ready(hc)) {
        return;
    }

    if (znx_buf_chain_total_bytes(request->output_chain) > 0 ||
        znx_buf_chain_total_bytes(request->body_chain))
    {
        znx_event_loop_post_event(&hc->c->write);
    }
}


void
znx_http_set_send_request_body_done(znx_http_request_t *request)
{
    request->last_body_flag = 1;
}


void
znx_http_disable_send_request_body(znx_http_request_t *request)
{
    znx_http_conn_t         *hc;

    hc = request->hc;

    if (!request->client_request) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "expect http client request");
    }

    if (!request->start_send_body) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "must exec start_send_request_body first");
    }

    znx_event_loop_del_timer(&hc->c->write);
    znx_http_conn_disable_write(hc);
}


static void
znx_http_try_switch_response_header_buf(znx_http_request_t *request, size_t buf_size)
{
    znx_buf_t                   *header_buf;
    znx_http_readline_sm_t      *readline_sm;
    znx_http_response_t         *response;
    size_t                      len = 0;

    response = request->response;

    buf_size += ZNX_HTTP_MIN_WRITEABLE_SIZE;
    header_buf = response->last_buf;
    readline_sm = &request->readline_sm;

    // has unparsed data, no need switch buf
    if (header_buf && header_buf->end != header_buf->pos) {
        return;
    }

    // avoid writeing in a spall space
    if (header_buf && header_buf->data_end - header_buf->end > ZNX_HTTP_MIN_WRITEABLE_SIZE) {
        return;
    }

    if (header_buf) {
        len = (size_t)(header_buf->end - readline_sm->line.data);
    }

    header_buf = znx_buf_alloc_ext(buf_size);
    response->last_buf = header_buf;
    znx_buf_chain_enqueue(response->header_chain, header_buf, ZNX_FALSE);

    if (len == 0) {
        return;
    }

    // copy and reset readline_sm
    znx_strncpy(header_buf->end, readline_sm->line.data, len);
    header_buf->end += len;
    znx_http_readline_sm_reset(readline_sm);
}


static znx_result_t
znx_http_response_process_headers(znx_http_request_t *request)
{
    size_t                      index;
    znx_http_response_t         *response;
    znx_http_header_t           *header;
    znx_http_conn_t             *hc;

    response = request->response;
    hc = request->hc;


    if (znx_http_method_is_head(&request->method)) {
        znx_http_headers_del(&response->headers, &znx_http_transfer_encoding_hdr_name);
        response->nobody = 1;
    }

    if (!znx_http_body_allowed_for_status(response->status_code)) {
        znx_http_headers_del(&response->headers, &znx_http_transfer_encoding_hdr_name);
        znx_http_headers_del(&response->headers, &znx_http_content_length_hdr_name);
        response->nobody = 1;
    }

    int64_t content_length = -1;
    header = znx_http_headers_get(&response->headers, &znx_http_content_length_hdr_name, 0);
    if (header != NULL) {
        if (!znx_str_to_int64(&header->val, &content_length) || content_length < 0) {
            ZNX_HTTP_LOG_ERROR(hc->hlog, "invalid content length header");
            return ZNX_HTTP_INVALID_CONTENT_LENGTH_HEADER;
        }

        response->content_length = content_length;
        response->content_remain = content_length;
    }

    header = znx_http_headers_get(&response->headers, &znx_http_transfer_encoding_hdr_name, 0);
    if (header != NULL) {
        if (header->val.len == ZNX_HTTP_CHUNKED_LEN &&
            znx_strncasecmp(header->val.data, ZNX_HTTP_CHUNKED, ZNX_HTTP_CHUNKED_LEN) == 0)
        {
            response->chunked = 1;
        } else if (header->val.len != ZNX_HTTP_IDENTITY_LEN &&
            znx_strncasecmp(header->val.data, ZNX_HTTP_IDENTITY, ZNX_HTTP_IDENTITY_LEN) == 0)
        {
            ZNX_HTTP_LOG_ERROR(hc->hlog, "transfer-encoding: %v not implemented", &header->val);
            return ZNX_HTTP_ERR_NOT_IMPLEMENTED;
        }

        if (request->http_version == ZNX_HTTP_VERSION_10) {
            response->chunked = 0;
            znx_http_headers_del(&response->headers, &znx_http_transfer_encoding_hdr_name);
        }
    }

    if (!response->nobody && response->content_length < 0 && !response->chunked) {
        // unbounded body
        request->want_close = 1;
    }

    index = 0;
    header = znx_http_headers_get(&response->headers, &znx_http_trailer_hdr_name, 0);
    if (header != NULL) {
        if (!response->chunked) {
            znx_http_headers_del(&response->headers, &znx_http_trailer_hdr_name);
        } else {
            response->trailers = znx_http_trailers_create(request->pool);
            while (header) {
                znx_http_parse_trailer_header(request->pool, response->trailers, &header->val);
                index++;
                header = znx_http_headers_get(&response->headers, &znx_http_trailer_hdr_name, index);
            }
        }
    }

    if (request->http_version < ZNX_HTTP_VERSION_11) {
        request->want_close = 1;
    }

    header = znx_http_headers_get(&response->headers, &znx_http_connection_hdr_name, 0);
    if (header != NULL) {
        if (header->val.len == ZNX_HTTP_CLOSE_LEN &&
            znx_strncmp(header->val.data, ZNX_HTTP_CLOSE, ZNX_HTTP_CLOSE_LEN) == 0)
        {
            request->want_close = 1;
        }
    }

    // the read data may contain body data.
    znx_buf_t           *body_buf;
    body_buf = response->last_buf;
    if (body_buf && body_buf->pos != body_buf->end) {
        response->body_buf = znx_buf_alloc(body_buf->data, body_buf->pos, body_buf->end);
    }

    // last_buf is the shadow of body_buf.
    // it just tracks body_buf->pos.
    response->last_buf = znx_pool_calloc(request->pool, sizeof(znx_buf_t));
    if (body_buf) {
        response->last_buf->pos = body_buf->pos;
    }

    return ZNX_FAILED;
}


static znx_result_t
znx_http_do_read_response_header(znx_http_request_t *request)
{
    znx_buf_t                   *header_buf;
    znx_http_readline_sm_t      *readline_sm;
    znx_http_header_t           tmp;
    znx_result_t                res;
    znx_http_core_conf_t        *cf;
    znx_http_header_t           *header;
    size_t                      n;
    znx_http_conn_t             *hc;
    znx_http_response_t         *response;

    cf = request->cf;
    response = request->response;
    readline_sm = &request->readline_sm;
    hc = request->hc;

    for ( ;; ) {
        znx_http_try_switch_response_header_buf(request, cf->response_header_buf_size);

        header_buf = response->last_buf;

        // no more data, read it.
        if (header_buf->pos == header_buf->end) {
            n = (size_t)(header_buf->data_end - header_buf->end);
            res = znx_http_conn_read(request->hc, header_buf->end, n);
            if (res == ZNX_AGAIN) {
                ZNX_HTTP_LOG_DEBUG(hc->hlog, "read response header again");
            }

            if (res == 0) {
                ZNX_HTTP_LOG_DEBUG(hc->hlog, "server close write in read response header");
                return ZNX_HTTP_SERVER_CLOSE_IN_READ_HEADER;
            }

            if (res < 0) {
                ZNX_HTTP_LOG_DEBUG(hc->hlog, "read response header failed, err: %d", res);
                return ZNX_HTTP_READ_HEADER_FAILED;
            }

            header_buf->end += res;
        }

        // read line
        res = znx_http_readline(readline_sm, header_buf);
        if (res == ZNX_AGAIN) {
            continue;
        }

        if (res != ZNX_SUCCESS) {
            ZNX_HTTP_LOG_DEBUG(hc->hlog, "found big header line");
            return ZNX_HTTP_BIG_HEADER_LINE;
        }

        response->header_size += readline_sm->line.len;

        if (response->header_size > cf->max_response_header_size) {
            ZNX_HTTP_LOG_DEBUG(hc->hlog, "server response header is too big");
            return ZNX_HTTP_RESPONSE_HEADER_TOO_LARGER;
        }

        if (znx_http_empty_line(readline_sm)) {
            if (!response->parse_rl_done) {
                return ZNX_HTTP_INVALID_RESPONSE_LINE;
            }

            response->parse_rh_done = 1;
            return ZNX_SUCCESS;
        }

        if (!response->parse_rl_done) {
            res = znx_http_parse_response_line(request);
            if (res != ZNX_SUCCESS) {
                ZNX_HTTP_LOG_DEBUG(hc->hlog, "parse response line failed, err: %d", res);
                return res;
            }

            response->parse_rl_done = 1;
            continue;
        }

        bzero(&tmp, sizeof(znx_http_header_t));
        res = znx_http_parse_header_line(&request->readline_sm.line, &tmp);
        if (res != ZNX_SUCCESS) {
            if (cf->ignore_invalid_headers) {
                response->invalid_headers++;
                continue;
            }
        }

        header = znx_pool_calloc(request->pool, sizeof(znx_http_header_t));
        header->key = tmp.key;
        header->val = tmp.val;

        znx_http_headers_add(&response->headers, header);
    }
}


static void
znx_http_read_response_header_handler(znx_http_conn_t *hc)
{
    znx_http_request_t          *request;
    znx_http_response_t         *response;
    znx_result_t                res;

    request = hc->data;
    response = request->response;

    ZNX_HTTP_LOG_INFO(hc->hlog, "http read response handler callback");

    if (hc->c->read.timedout) {
        ZNX_HTTP_LOG_DEBUG(hc->hlog, "http read response header timedout");
        hc->c->read.timedout = 0;
        request->read_error = ZNX_HTTP_READ_HEADER_TIMEOUT;
        znx_event_loop_del_timer(&hc->c->read);
        znx_http_conn_disable_read(hc);
        response->handler(request, ZNX_HTTP_READ_HEADER_TIMEOUT);
        return;
    }

    res = znx_http_do_read_response_header(request);

    // again, wait read event callback
    if (res == ZNX_AGAIN) {
        return;
    }

    // success or failed
    znx_event_loop_del_timer(&hc->c->read);
    znx_http_conn_disable_read(hc);

    if (res != ZNX_SUCCESS) {
        request->read_error = res;
        response->handler(request, res);
        return;
    }

    res = znx_http_response_process_headers(request);
    if (res != ZNX_SUCCESS) {
        request->read_error = res;
        response->handler(request, res);
        return;
    }

    response->handler(request, ZNX_SUCCESS);
}


void
znx_http_start_read_response_headers(znx_http_request_t *request,
    znx_http_request_handler_t done_handler, void *data)
{
    znx_http_conn_t             *hc;
    znx_http_core_conf_t        *cf;
    znx_http_response_t         *response;

    cf = request->cf;
    hc = request->hc;
    response = request->response;

    if (!request->client_request) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "expect http client request");
    }

    if (request->read_error != ZNX_SUCCESS) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "an error has occurred in http request");
    }

    if (response->start_read_header) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "repeat call start_read_header");
    }

    response->start_read_header = 1;
    response->handler = done_handler;
    response->data = data;

    hc->read_handler = znx_http_read_response_header_handler;
    znx_event_loop_add_timer(&hc->c->read, cf->read_response_header_timeout_ms);
    znx_http_conn_start_io(hc);

    // reuse request->readline_sm
    request->readline_sm.max_line_size = cf->response_header_buf_size;
    znx_http_read_response_header_handler(hc);
}


static znx_result_t
znx_http_do_read_response_body(znx_http_request_t *request)
{
    return ZNX_FAILED;
}


static void
znx_http_read_response_body_handler(znx_http_conn_t *hc)
{
    znx_result_t                    res;
    znx_http_request_t              *request;
    znx_http_response_t             *response;
    znx_http_core_conf_t            *cf;

    request = hc->data;
    response = request->response;
    cf = request->cf;

    // if res == ZNX_SUCCESS, this means response body read done.
    // if res == ZNX_AGAIN:
    // *1. the data may have been read.
    // *2. uneconsumed body data reaches the maximum value.
    // *3. reading operation encountered AGAIN.
    // other errors.
    res = znx_http_do_read_response_body(request);

    // in again scene, no body data, no callback needed.
    if (res == ZNX_AGAIN &&
        znx_buf_chain_total_bytes(response->body_chain) == 0)
    {
        znx_event_loop_add_timer(&hc->c->read, cf->read_response_body_timeout_ms);
        znx_http_enable_read_response_body(request);
        return;
    }

    if (res != ZNX_AGAIN && res != ZNX_SUCCESS) {
        request->read_error = res;
    }

    znx_http_disable_read_response_body(request);
    response->handler(request, res);
}


void
znx_http_start_read_response_body(znx_http_request_t *request,
    znx_http_request_handler_t notify_handler, void *data)
{
    znx_http_core_conf_t            *cf;
    znx_http_response_t             *response;

    cf = request->cf;
    response = request->response;

    if (!request->client_request) {
        ZNX_HTTP_LOG_FATAL(request->hc->hlog, "expect http client request");
    }

    if (!response->parse_rh_done) {
        ZNX_HTTP_LOG_FATAL(request->hc->hlog, "header has no been parsed");
    }

    if (response->start_read_body) {
        ZNX_HTTP_LOG_FATAL(request->hc->hlog, "repeat call start_read_body");
    }

    response->start_read_body = 1;

    if (request->read_error != ZNX_SUCCESS) {
        ZNX_HTTP_LOG_FATAL(request->hc->hlog, "an error has occurred in http response");
    }

    response->handler = notify_handler;
    response->data = data;
    request->hc->read_handler = znx_http_read_response_body_handler;
    request->readline_sm.max_line_size = cf->response_body_buf_size;
    znx_http_read_response_body_handler(request->hc);
}


void
znx_http_enable_read_response_body(znx_http_request_t *request)
{
    znx_http_conn_t             *hc;
    znx_http_response_t         *response;

    hc = request->hc;
    response = request->response;

    if (!request->client_request) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "expect http client request");
    }

    if (!response->start_read_body) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "must exec start read response body first");
    }

    if (request->read_error != ZNX_SUCCESS)
    {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "an error has occurred in http response");
    }

    if (response->read_body_done) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "http response body already read done");
    }

    if (!hc->read_active) {
        znx_http_conn_enable_read(hc);
    }

    // has data, schedule immediately.
    if (znx_http_conn_read_ready(hc) ||
        znx_buf_chain_total_bytes(response->body_chain) > 0 ||
        (response->body_buf && response->body_buf->pos != response->body_buf->end))
    {
        znx_event_loop_post_event(&hc->c->read);
    }
}


void
znx_http_disable_read_response_body(znx_http_request_t *request)
{
    znx_http_conn_t         *hc;
    znx_http_response_t     *response;

    hc = request->hc;
    response = request->response;

    if (!request->client_request) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "expect http client request");
    }

    if (!response->start_read_body) {
        ZNX_HTTP_LOG_FATAL(hc->hlog, "must exec start read response body first");
    }

    znx_event_loop_del_timer(&hc->c->read);
    znx_http_conn_disable_read(hc);
}
